#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include "trie.h"
#include "mpi.h"

typedef char word_t[6];

/* Transforma a string em caracteres minusculos */
char * strToAlnumLower (char * str)
{
    char * it = str;
    while (*it != '\0')
    {
        if (*it <= 'Z')
            *it += 32; /* 'a'-'A' = 32 */
        it++;
    }
    return str;
}

int main (int argc, char ** argv)
{

	const char * filename;
	int i, j, len, totalwords, found;
	char str[100];
	char str2[6];
	FILE * f;
	trie T;

	int rc, rank, size;
	word_t wordsbuffer[40000];
	word_t wordsbutterin[40000];
	int wfpos;

	/* Inicia as operações da MPI */
	rc = MPI_Init(&argc, &argv);
	if (rc != MPI_SUCCESS)
	{
		fprintf(stderr, "MPI nao pode iniciar corretamente.\n");
		return EXIT_FAILURE;
	}
	double firsttime = MPI_Wtime();
	MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* Pega o rank */
	MPI_Comm_size(MPI_COMM_WORLD, &size); /* Pega o número de processos */

	/* Verifica se os parâmetros estão corretos */
	if (argc != 2)
	{
		fprintf(stderr, "USO: %s texto_de_entrada.txt\n", argv[0]);
		return EXIT_FAILURE;
	}

	/* Cria a estrutura de dados para guardar as palavras */
	TrieCreate(&T);

	/* Nome do arquivo de texto para leitura */
	filename = argv[1];

	/* Abre o arquivo para leitura */
	f = fopen(filename, "rw");

	/* Lê as palavras e as insere na estrutura de dados */
	while(fscanf(f, "%[a-zA-Z]%*[^a-zA-Z]", str) != EOF)
	{
		len = strlen(str);
		if (len > 1)
		{
			/* Se a palavra tiver mais que 1 letra e tiver resto 1 na divisão por 5 */
			/* significa que a última parte ficará com 1 letra, o que não é permitido. */
			/* Então a primeira parte terá 4 letras e a última ficará com 2 letras. */
			if (len % 5 == 1)
			{
				for (i = 0; i < 4; i++)
					str2[i] = str[i];
				str2[i] = '\0';
				TrieAdd(&T, strToAlnumLower(str2));
				i = 4;
			}
			else
				i = 0;
			for (j = 0; i < len; i++, j++)
			{
				str2[j] = str[i];
				if (j == 4 || i+1 == len)
				{
					str2[j+1] = '\0';
					TrieAdd(&T, strToAlnumLower(str2));
					j = -1;
				}
			}
		}
		else
			TrieAdd(&T, strToAlnumLower(str));
	}

	totalwords = T.words;


	int tempo = (int) time(NULL);
	MPI_Bcast(&tempo, 1, MPI_INT, 0, MPI_COMM_WORLD);
	srand(tempo+rank);

	int head;
	int goal = 90;
	while (T.words)
	{
		/* BUSCA */
		found = 0;
		head = 0;
		wfpos = 0;
		for (j = 0; j < 1000000; j++)
		{
			for (i = 0; i < 5; i++)
				wordsbuffer[head][i] = rand()%26+'a';
			wordsbuffer[head][i] = '\0';
			if (TrieFind(&T, wordsbuffer[head]))
			{
				TrieRemove(&T, wordsbuffer[head]);
				if (!rank && T.words*100/totalwords == goal)
				{
					printf("%3d%% \t (%lf)\n", 90-goal, MPI_Wtime()-firsttime);
					goal -= 10;
				}
				head++;
			}
		}
		/* SINCRONIZAÇÃO */
		for (j = 0; j < size; j++)
		{
			int headin = head;
			MPI_Bcast(&headin, 1, MPI_INT, j, MPI_COMM_WORLD);
			
			word_t * wb;
			if (j == rank) wb = wordsbuffer;
			else wb = wordsbutterin;
			
			MPI_Bcast(wb, headin*6, MPI_CHAR, j, MPI_COMM_WORLD);

			for (i = 0; i < headin; i++)
			{
				TrieRemove(&T, wb[i]);
				if (!rank && T.words*100/totalwords == goal)
				{
					printf("%3d%% \t (%lf)\n", 90-goal, MPI_Wtime()-firsttime);
					goal -= 10;
				}
				found++;
			}
		}
	}
	if (!rank)
		printf("100%% \t (%lf)\n", MPI_Wtime()-firsttime);

	TrieDestroy(&T);

	/* Finaliza as operações da MPI */
	MPI_Finalize();
	
	return EXIT_SUCCESS;
}
